#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

#define NUM 1024
#define SIZE 32
#define SEP " "

// 保存用户输入的字符串
char cmd_line[NUM];

// 保存分割后的字符数组
char* g_argv[SIZE];

// 存放想要添加的环境变量
char g_val[32];

int main()
{
    // 0. 命令行解释器：一定是一个不退出的进程
    while (1)
    {
        // 1. 打印提示信息
        printf("[root@localhost myshell]# "); // 没有\n是不会打印的
        fflush(stdout); // 刷新缓冲区
        memset(cmd_line, '\0', sizeof(cmd_line));
        
        // 2. 获取输入的指令和选项
        if (fgets(cmd_line, sizeof(cmd_line), stdin) == NULL) // 判断
        {
            continue;
        }
        cmd_line[strlen(cmd_line) - 1] ='\0' ; // 输入的应该是：ls -l -a\n，要把\n去掉
        
        // 3. 命令行字符串解析
        g_argv[0] = strtok(cmd_line, SEP); // 第一次要传入字符串
        int index = 1;
        if (strcmp(g_argv[0], "ls") == 0)
        {
            g_argv[index++] = "--color=auto"; // 也可以为ls加上配色
        }
        if (strcmp(g_argv[0], "ll") == 0)
        {
            g_argv[0] = "ls";
            g_argv[index++] = "-l";
            g_argv[index++] = "--color=auto";
        }
        while (g_argv[index++] = strtok(NULL, SEP)); // 第二次如果还要解析上一个字符串就要传入NULL
        // export添加环境变量也是要在父进程添加的
        if (strcmp(g_argv[0], "export") == 0 && g_argv[1] != NULL)
        {
            strcpy(g_val, g_argv[1]);
            int ret = putenv(g_val);
            if (ret == 0) printf("export success\n");
            continue;
        }
        
        // 4. 处理内置命令，是让父进程（shell）执行的，是要影响父进程的
        if (strcmp(g_argv[0], "cd") == 0)
        {
            if (g_argv[1] != NULL)
                chdir(g_argv[1]);
            continue;
        }
        
        // 5. 创建子进程
        pid_t id = fork();
        if (id == 0)
        {
            // 子进程
            execvp(g_argv[0], g_argv);
            exit(1);
        }
        else 
        {
            // 父进程
            int status = 0;
            pid_t ret = waitpid(id, &status, 0); // 阻塞等待
            if (ret > 0) 
                printf("exit_code: %d\n", WEXITSTATUS(status));
        }
    }
}
